home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 436_01 / infloat.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-07  |  14.3 KB  |  557 lines

  1. /*************************************************************************
  2.     Source file:  INFLOAT.C
  3.  
  4.     INCON floating-point input handler.
  5.  
  6.     Compiler:  Borland Turbo C 2.01
  7.  
  8.     INCON source files and the object and library files created from
  9.     them are:
  10.         Copyright (c) 1993-94, Richard Zigler.
  11.     You may freely distribute unmodified source, object, and library
  12.     files, and incorporate them into your own non-commercial software,
  13.     provided that this paragraph and the program name and copyright
  14.     strings defined in INCON.C are included in all copies.
  15. *************************************************************************/
  16.  
  17. #include <ctype.h>                            /* character classification        */
  18. #include <string.h>
  19. #include "indefs.h"                            /* globals and definitions            */
  20. #include "incon.h"                            /* state definitions                    */
  21. #include "indecl.h"                            /* public utility routines            */
  22.  
  23. /**** Local Data ****/
  24.  
  25. enum                                                /* float field fix-up constants    */
  26.             {
  27.             FLT_INIT                        ,        /* initialize field for display    */
  28.             FLT_FIX                        ,        /* fix field left of decimal        */
  29.             FLT_MOV                        ,        /* move chars left of decimal        */
  30.             FLT_PAD                        ,        /* pad out field with zeros        */
  31.             }                                ;
  32.  
  33. static BFLAG                Point        ;        /* decimal point entered yet?        */
  34. static short                PointNdx    ;        /* decimal point index                */
  35. static BFLAG                Sign        ;        /* sign entered yet?                    */
  36. static short                SignNdx    ;        /* numeric sign index                */
  37.  
  38. /**** Local Routines ****/
  39.  
  40. static int near pascal    FloatFix
  41.                 (
  42.                 int            Op,                /* operation requested                */
  43.                 int            Pos                /* index into string                    */
  44.                 )                ;
  45.  
  46. int pascal hFloatField( STATES State, register int Pos )
  47. {
  48. register int    max_width = Width - 1;
  49. int                old_pos = Pos;
  50.  
  51. switch( State )
  52.     {
  53.     case stError:
  54.         break;
  55.  
  56.     case stQuit:                                /* [Enter] -- end input                */
  57.         Chr = 0;
  58.         goto __FloatExit;
  59.  
  60.     case stInit:                                /* field initialization                */
  61.         SignNdx = 0;
  62.         Point = Sign = NO;
  63.         StrLength = Width;
  64.         if ( !Prec )
  65.             {
  66.             Prec = strchr( OutStr, '.' ) - OutStr;
  67.             Prec = StrLength - Prec - 1;
  68.             }
  69.         PointNdx = Width - Prec - 1;
  70.         if ( !PointNdx )
  71.             {
  72.             --Prec;                                /* force at least one decimal        */
  73.             ++PointNdx;                            /*  digit position                    */
  74.             }
  75.         if ( PointNdx > 0 )
  76.             {
  77.             Flags.Confirm    =
  78.             Flags.Display    = YES;
  79.             Pos = FloatFix( FLT_INIT, 0 );
  80.             }
  81.         return ( Pos );
  82.  
  83.     case stFieldClear:                        /* [Esc] -- clear field or exit    */
  84.         if ( EscSet)
  85.             More = NO;
  86.         else
  87.             {
  88.             memset( OutStr, Fill, Width );
  89.             OutStr[PointNdx] = '.';
  90.             Pos        =
  91.             SignNdx    =
  92.             Sign        = 0;
  93.             ++Update;
  94.             }
  95.         break;
  96.  
  97.     case stDeleteCharLeft:                    /* [Backspace] -- delete left        */
  98.         if ( Pos )
  99.             {
  100.             --Move;
  101.             if ( Pos == max_width && OutStr[Pos] != (char)Fill )
  102.                 OutStr[Pos] = Fill;
  103.             else if ( --Pos == PointNdx )
  104.                 OutStr[--Pos] = Fill;
  105.             else
  106.                 {
  107.                 if ( Pos < PointNdx )
  108.                     max_width = PointNdx - 1;
  109.                 goto __MoveCharsToCursor;
  110.                 }
  111.             }
  112.         break;
  113.  
  114.     case stMoveToStart:                        /* [Home] -- start of field        */
  115.         if ( Pos )
  116.             {
  117.             Pos = 0;
  118.             --Move;
  119.             }
  120.         break;
  121.  
  122.     case stMoveCharLeft:                        /* [Left Arrow] -- move char lt    */
  123.         if ( Pos )
  124.             {
  125.             if ( --Pos <= SignNdx )
  126.                 Pos = 0;
  127.             --Move;
  128.             }
  129.         break;
  130.  
  131.     case stMoveCharRight:                    /* [Right Arrow] -- move char rt    */
  132.         if ( Pos < max_width )
  133.             {
  134.             ++Pos;
  135.             ++Move;
  136.             }
  137.         break;
  138.  
  139.     case stMoveToEnd:                            /* [End] -- end of field            */
  140.         if ( Pos < max_width )
  141.             {
  142.             Pos = max_width;
  143.             ++Move;
  144.             }
  145.         break;
  146.  
  147.     case stMoveWordLeft:                        /* [Ctrl Left] -- move word lt    */
  148.         if ( Pos )
  149.             {
  150.             Pos = Pos > PointNdx + 1 ? PointNdx + 1 : 0 ;
  151.             --Move;
  152.             }
  153.         break;
  154.  
  155.     case stMoveWordRight:                    /* [Ctrl Right] -- move word rt    */
  156.         if ( Pos < max_width )
  157.             {
  158.             Pos = Pos < PointNdx ? PointNdx + 1 : max_width ;
  159.             ++Move;
  160.             }
  161.         break;
  162.  
  163.     case stDeleteWordLeft:                    /* [Ctrl L] -- delete word left    */
  164.         if ( Pos == (PointNdx + 1) )
  165.             Pos = PointNdx - 1;
  166.         max_width = Pos;
  167.         Pos = Pos > PointNdx ? PointNdx + 1 : 0 ;
  168.         goto __DeleteWord;
  169.  
  170.     case stDeleteWordRight:                    /* [Ctrl R] -- delete word right    */
  171.         if ( Pos == (PointNdx - 1) )
  172.             Pos = PointNdx + 1;
  173.         if ( Pos < PointNdx )
  174.             max_width = PointNdx - 1;        /* fall through to __DeleteWord    */
  175.  
  176.     case stDeleteToEnd:                        /* [Ctrl End] -- clear to end        */
  177.  
  178. __DeleteWord:
  179. ;
  180.         memset( OutStr + Pos, Fill, max_width - Pos + 1 );
  181.         OutStr[PointNdx] = '.';
  182.         ++Update;
  183.         break;
  184.  
  185.     case stDeleteToStart:                    /* [Ctrl Home] -- clear to start    */
  186.         max_width = Pos;
  187.         Pos = 0;
  188.         goto __DeleteWord;
  189.  
  190.     case stDeleteAtCursor:                    /* [Del] -- delete at cursor        */
  191.         if ( Pos < PointNdx )
  192.             max_width = PointNdx - 1;
  193.         ++Update;
  194.  
  195. __MoveCharsToCursor:
  196. ;
  197.         memcpy( OutStr + Pos, OutStr + Pos + 1, max_width - Pos );
  198.         OutStr[max_width] = Fill;
  199.         break;
  200.  
  201.     case stExitPlus:                            /* [Keypad +] -- special exit        */
  202.     case stExitMinus:                            /* [Keypad -] -- special exit        */
  203.  
  204.         /****
  205.             If cursor is to right of sign position or Sign flag
  206.             is off, treat these keys as exits; otherwise convert
  207.             Chr to '+' or '-' and fall through to stCharMatch.
  208.         ****/
  209.  
  210.         if ( Pos > SignNdx || !Flags.Sign )
  211.             {
  212.  
  213. __FloatExit:
  214. ;
  215.             FloatFix( FLT_FIX, PointNdx    );
  216.             FloatFix( FLT_PAD, 0                );
  217.             More = NO;
  218.             ++Update;
  219.             break;
  220.             }
  221.         else
  222.             Chr = (State == stExitPlus) ? '+' : '-' ;
  223.  
  224.     case stCharMatch:                            /* see if entry matches field        */
  225.         if
  226.             (
  227.             isdigit( Chr )
  228.             ||
  229.             ( (Chr == '+' || Chr == '-') && Pos <= SignNdx && Flags.Sign )
  230.             ||
  231.             ( !Point && Chr == '.' )
  232.             )
  233.             {
  234.             if ( InBegin )
  235.                 Pos = hFloatField( stFieldClear, 0 );
  236.             if ( Chr == '+' || Chr == '-' )
  237.                 {
  238.                 Sign        = Chr;
  239.                 SignNdx    = Pos;
  240.                 }
  241.             if ( Chr == '.' )
  242.                 {
  243.                 FloatFix( FLT_FIX, Pos );
  244.                 Pos = PointNdx + 1;
  245.                 memset( OutStr + Pos, Fill, Prec );
  246.                 }
  247.             else
  248.                 OutStr[Pos++] = Chr;
  249.             ++Move;
  250.          }                                        /* if (isdigit)                        */
  251.         else
  252.             State = stError;
  253.         break;
  254.     }                                                /* switch                                */
  255.  
  256. if ( Pos >= Width )
  257.     Pos = Width - 1;
  258. if ( Pos == PointNdx )
  259.     Pos += Move;                                /* move left or right past sign    */
  260. Point = (Pos >= PointNdx);
  261.  
  262. if ( Move )
  263.     {
  264.  
  265.     /****
  266.         If the cursor has passed to the left of the sign (or
  267.         implied sign) position, FloatFix() moves the integer
  268.         portion of OutStr to the beginning of the field and
  269.         fills vacant digit positions with Fill.
  270.     ****/
  271.  
  272.     if ( Pos <= SignNdx )
  273.         Pos = FloatFix( FLT_MOV, PointNdx );
  274.  
  275.     /****
  276.         If the cursor has passed to the right over the decimal
  277.         point, FloatFix() closes gaps in the integer portion of
  278.         OutStr and moves that part of the string adjacent to the
  279.         point.  After the call to FloatFix(), all fill characters
  280.         to the left of the decimal point have been replaced by
  281.         digits, sign, or spaces, so FloatFix() is not called on
  282.         subsequent moves while the cursor is still to the right
  283.         of the decimal.
  284.     ****/
  285.  
  286.     if    ( Move > 0 && Pos > PointNdx )
  287.         {
  288.         if ( memchr( OutStr, Fill, PointNdx ) )
  289.             FloatFix( FLT_FIX, PointNdx );
  290.         }
  291.  
  292.     /****
  293.         If the cursor has passed to the left over the decimal
  294.         point, FloatFix() closes gaps in the decimal portion
  295.         of OutStr and pads it with zeros.  After the call to
  296.         FloatFix(), all fill characters to the right of the
  297.         decimal point have been replaced by digits, so
  298.         FloatFix() is not called on subsequent moves while
  299.         the cursor is still to the left of the decimal.
  300.     ****/
  301.  
  302.     else if ( Move < 0 && Pos < PointNdx && old_pos > PointNdx )
  303.         {
  304.         if ( memchr( OutStr + PointNdx + 1, Fill, Prec ) )
  305.             FloatFix( FLT_PAD, PointNdx );
  306.         }
  307.     ++Update;
  308.     Move = NO;
  309.    }                                                /* if(Move)                                */
  310. return( Pos );
  311. }                                                    /**** hFloatField()                ****/
  312.  
  313. /*************************************************************************
  314.     FloatFix()
  315.     Patches up the output string in floating-point fields.
  316. *************************************************************************/
  317.  
  318. static int near pascal FloatFix
  319. (
  320.             int        Op,                        /* operation requested                */
  321. register int        Pos                        /* index into string                    */
  322. )
  323. {
  324.             BYTE        chr;                        /* char from input string            */
  325. register int        s_ptr;                    /* alternate index                    */
  326.             int        work;                        /* integer working storage            */
  327.  
  328. switch ( Op )
  329.     {
  330.     case FLT_INIT:
  331.  
  332.         memset( OutStr, Fill, Width );
  333.         OutStr[PointNdx] = '.';
  334.         SignNdx    =
  335.         Sign        = 0;
  336.  
  337.         /****
  338.             If the default input string is not to be displayed in
  339.             the input field then initialization is complete.  If
  340.             it is to be displayed, each character must be examined
  341.             and put in its proper place, since the string may not
  342.             be formatted like the field.
  343.         ****/
  344.  
  345.         if ( !Flags.Display )
  346.             break;
  347.         else
  348.             {
  349.             for ( Pos = 0 ; Pos <= PointNdx ; Pos++ )
  350.                 {
  351.                 chr = InStr[Pos];                /* get char from default string    */
  352.  
  353.                 /****
  354.                     If the integer portion of OutStr has been filled
  355.                     but the default string decimal point hasn't been
  356.                     found, use work to index the decimal portion of
  357.                     the default string.  Advance work beyond the
  358.                     default string decimal point, discarding any
  359.                     remaining integer digits in the default string.
  360.                 ****/
  361.  
  362.                 if ( Pos == PointNdx )
  363.                     {
  364.                     work = Pos;
  365.                     do
  366.                         chr = InStr[work++];
  367.                     while ( chr && chr != '.' );
  368.                     }
  369.  
  370.                 else if ( isdigit( chr ) )        /* copy digits to OutStr        */
  371.                     OutStr[Pos] = chr;
  372.  
  373.                 /****
  374.                     If a sign character is found in the integer
  375.                     portion of the default string, force signed
  376.                     input and set the index and flags accordingly.
  377.                     No attempt is made here to make sense of some
  378.                     such string as "+12-34.56".  The last sign in
  379.                     the default string determines the ultimate
  380.                     setting of index and flags; any characters
  381.                     preceding that will be discarded at FLT_FIX.
  382.                 ****/
  383.  
  384.                 else if ( chr == '+' || chr == '-' )
  385.                     {
  386.                     Flags.Sign    = YES;
  387.                     SignNdx        = Pos;
  388.                     Sign            =
  389.                     OutStr[Pos]    = chr;
  390.                     }
  391.  
  392.                 /****
  393.                     Pos is still in the integer portion of OutStr
  394.                     but a decimal or the end-of-string null has
  395.                     been found in the default string.  Move Pos
  396.                     to the decimal point in OutStr so that for()
  397.                     fails.  Pos will be incremented at the bottom
  398.                     of the for loop prior to exit, so it will wind
  399.                     up pointing at the position following the decimal
  400.                     point in OutStr.  Use work to index the remaining
  401.                     digits of the default string, if any.
  402.                 ****/
  403.  
  404.                 else if ( !chr || chr == '.' )
  405.                     {
  406.                     work = Pos + 1;
  407.                     Pos = PointNdx;
  408.                     }
  409.                 }                                    /* for (Pos)                            */
  410.  
  411.             /*******************************************************
  412.                 At this point, for() has failed for one of
  413.                 three reasons:  encountered the end of the
  414.                 default string; found the default string
  415.                 decimal; or reached the decimal position
  416.                 in OutStr.  In the first case, the while()
  417.                 test below will fail immediately.  The two
  418.                 remaining cases are illustrated here.  Pos
  419.                 and work are shown as they have been set
  420.                 above; 'd' represents a decimal integer.
  421.             --------------------------------------------------------
  422.                 Found the default string decimal:
  423.  
  424.                     default:  1 2 3 . 4 5
  425.                                  work __/
  426.  
  427.                      OutStr:  1 2 3 d . d d d d
  428.                                      Pos __/
  429.  
  430.                 OutStr will be adjusted at FLT_FIX.
  431.             --------------------------------------------------------
  432.                 Reached the decimal point in OutStr:
  433.  
  434.                     default:  1 2 3 4 5 . 6 7
  435.                                       work __/
  436.  
  437.                      OutStr:  1 2 3 4 . d d d d
  438.                                      Pos __/
  439.  
  440.                 The extra integer digit in the default string
  441.                 is discarded.
  442.             *******************************************************/
  443.  
  444.             /****
  445.                 Copy digits from default to OutStr until either
  446.                 OutStr is filled or the end of the default string
  447.                 is reached.  Any non-digit characters that may be
  448.                 lurking in the default string are discarded.  At
  449.                 this point, chr will contain either the default
  450.                 string decimal point or end-of-string null.
  451.             ****/
  452.  
  453.             while ( chr && Pos < Width )
  454.                 {
  455.                 if ( isdigit( chr ) )
  456.                     OutStr[Pos++] = chr;
  457.                 chr = InStr[work++];
  458.                 }
  459.  
  460.             /****
  461.                 Now fall through to FLT_FIX to prettify the output
  462.                 string for display.
  463.             ****/
  464.  
  465.             }                                        /* if(Flags.Display)                    */
  466.  
  467.     case FLT_FIX :
  468.  
  469.         /****
  470.             Move the integer portion of OutStr adjacent to the
  471.             decimal point.  Pad the beginning of the string with
  472.             spaces.
  473.         ****/
  474.  
  475.         s_ptr = PointNdx;
  476.         if ( Pos > s_ptr )
  477.             Pos = s_ptr;
  478.  
  479.         while ( Pos )
  480.             {
  481.             chr = OutStr[--Pos];
  482.  
  483.             /****
  484.                 Copy digits to s_ptr.  Pos is decremented with each
  485.                 pass through the loop but s_ptr is decremented only
  486.                 when a valid character is found, so it indexes the
  487.                 decimal until the first character is copied.
  488.             ****/
  489.  
  490.             if ( isdigit( chr ) )
  491.                 OutStr[--s_ptr] = chr;
  492.  
  493.             /****
  494.                 Copy sign to s_ptr.  If s_ptr has not moved from the
  495.                 decimal there are no digits between the decimal and
  496.                 the sign.  If there is room for it, insert a zero
  497.                 between the sign and the decimal point and mark the
  498.                 new position of the sign.
  499.             ****/
  500.  
  501.             else if ( chr == Sign && Pos == SignNdx )
  502.                 {
  503.                 if ( s_ptr == PointNdx && s_ptr > 1 )
  504.                     OutStr[--s_ptr] = '0';
  505.                 OutStr[--s_ptr] = chr;
  506.                 SignNdx = s_ptr;
  507.                 break;
  508.                 }
  509.             }                                        /* while (Pos)                            */
  510.  
  511.         if ( s_ptr == PointNdx && s_ptr > 0 )        /* if no digits/sign,    */
  512.             OutStr[--s_ptr] = '0';                        /* insert 0 if possible    */
  513.         for ( Pos = 0 ; Pos < s_ptr ; Pos++ )        /* blank shifted sign    */
  514.             OutStr[Pos] = ' ';                            /*  and digits                */
  515.         if ( SignNdx < s_ptr )                            /* index the sign or        */
  516.             SignNdx = s_ptr;                                /*  the implied sign        */
  517.         break;
  518.  
  519.     case FLT_MOV :
  520.  
  521.         /****
  522.             Move the integer portion of OutStr to the beginning
  523.             of the input field.
  524.         ****/
  525.  
  526.         Pos = PointNdx - SignNdx;
  527.         if ( Pos > 0 )
  528.             {
  529.             memcpy( OutStr, OutStr + SignNdx, Pos );
  530.             memset( OutStr + Pos, Fill, SignNdx );
  531.             }
  532.         Pos = SignNdx = 0;
  533.         break;
  534.  
  535.     case FLT_PAD :
  536.  
  537.         /****
  538.             Close gaps between digits in the decimal portion of
  539.             OutStr and pad the end of the string with zeros.
  540.         ****/
  541.  
  542.         Pos = s_ptr = PointNdx + 1;
  543.         while ( Pos < Width )
  544.             {
  545.             chr = OutStr[Pos++];
  546.             if ( isdigit( chr ) )
  547.                 OutStr[s_ptr++] = chr;
  548.             }
  549.         while ( s_ptr < Width )
  550.             OutStr[s_ptr++] = '0';
  551.         break;
  552.     }                                                /* switch (Op)                            */
  553. return( Pos );
  554. }                                                    /**** FloatFix()                    ****/
  555.  
  556. /**** EOF:  INFLOAT.C ****/
  557.